/**********************************************************************
*  Copyright (c) 2007 - Victor Jacobs - victor.jacobs@gmail.com
*
*  Permission is hereby granted, free of charge, to any person
*  obtaining a copy of this software and associated documentation
*  files (the "Software"), to deal in the Software without
*  restriction, including without limitation the rights to use,
*  copy, modify, merge, publish, distribute, sublicense, and/or sell
*  copies of the Software, and to permit persons to whom the
*  Software is furnished to do so, subject to the following
*  conditions:
*
*  The above copyright notice and this permission notice shall be
*  included in all copies or substantial portions of the Software.
*
*  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
*  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
*  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
*  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
*  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
*  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
*  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
*  OTHER DEALINGS IN THE SOFTWARE.
**********************************************************************/

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Tao.Ode;
using Tao.OpenGl;
using Vj.MyMath;
using Vj.OdeObjectLibrary;
using Vj.ShapeLibrary;

namespace Vj.ODEBalls
{
    public partial class MainForm : Form
    {
        private Grid floor;

        private Quad myBox1;
        private Quad myBox2;

        private static IntPtr world;
        private IntPtr space;

        private static IntPtr contactgroup;
        private static Ode.dContactGeom[] contacts;

        public MainForm()
        {
            InitializeComponent();
        }

        #region On Load

        private void simpleOpenGlControl1_Load(object sender, EventArgs e)
        {
            simpleOpenGlControl1.InitializeContexts();
            simpleOpenGlControl1.AutoSwapBuffers = true;

            InitGL();
            SetLights();

            InitializeODEWorld();

            simpleOpenGlControl1_Resize(null, null);
        }

        #endregion

        #region Initialization

        private void simpleOpenGlControl1_Resize(object sender, EventArgs e)
        {
            int w = simpleOpenGlControl1.Width;
            int h = simpleOpenGlControl1.Height;

            float ratio = ((float)w / (float)h);

            // set viewport to current window size
            Gl.glViewport(0, 0, w, h);

            // set up prespective projection
            Gl.glMatrixMode(Gl.GL_PROJECTION);
            Gl.glLoadIdentity();
            Glu.gluPerspective(90, ratio, 1, 500);
            //Glu.gluLookAt(0.0, 0.0, 30.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);

            // set up model view matrix
            Gl.glMatrixMode(Gl.GL_MODELVIEW);
            Gl.glLoadIdentity();
        }

        private static bool InitGL()
        {
            Gl.glShadeModel(Gl.GL_SMOOTH);
            Gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
            Gl.glClearDepth(1);
            Gl.glEnable(Gl.GL_DEPTH_TEST);
            Gl.glDepthFunc(Gl.GL_LEQUAL);
            Gl.glHint(Gl.GL_PERSPECTIVE_CORRECTION_HINT, Gl.GL_NICEST);
            Gl.glEnable(Gl.GL_LIGHT0);
            Gl.glEnable(Gl.GL_LIGHT1);
            Gl.glEnable(Gl.GL_LIGHTING);
            Gl.glEnable(Gl.GL_NORMALIZE);

            return true;
        }

        private void SetLights()
        {
            // global light
            float[] lmodel_ambient = { 0.75f, 0.75f, 0.75f, 1.0f };
            Gl.glLightModelfv(Gl.GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);

            float[] lightAmbient = { 0.4f, 0.4f, 0.4f, 1.0f };
            float[] lightDiffuse = { 0.6f, 0.6f, 0.6f, 1.0f };
            float[] lightPosition = { -50.0f, 20.0f, 10.0f, 1.0f };
            float[] lightPosition2 = { 50.1f, 20.0f, 10.0f, 1.0f };


            Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_POSITION, lightPosition);
            //Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_SPECULAR, lightSpecular);
            Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_AMBIENT, lightAmbient);
            Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_DIFFUSE, lightDiffuse);

            Gl.glLightfv(Gl.GL_LIGHT1, Gl.GL_POSITION, lightPosition2);
            //Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_SPECULAR, lightSpecular);
            Gl.glLightfv(Gl.GL_LIGHT1, Gl.GL_AMBIENT, lightAmbient);
            Gl.glLightfv(Gl.GL_LIGHT1, Gl.GL_DIFFUSE, lightDiffuse);
        }

        #endregion

        #region Paint

        private void simpleOpenGlControl1_Paint(object sender, PaintEventArgs e)
        {
            Gl.glClear(Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT);
            Gl.glLoadIdentity();

            Gl.glPushMatrix();

            Gl.glTranslated(xTransScrollBar.Value,
                                     yTransScrollBar.Value,
                                     zTransScrollBar.Value - 15.0f);

            Gl.glRotatef(xRotScrollBar.Value, 1.0f, 0.0f, 0.0f);
            Gl.glRotatef(yRotScrollBar.Value, 0.0f, 1.0f, 0.0f);
            Gl.glRotatef(zRotScrollBar.Value, 0.0f, 0.0f, 1.0f);

            if (myBox1 != null)
                myBox1.Render();

            if (myBox2 != null)
                myBox2.Render();

            floor.Draw();
            Gl.glPopMatrix();
        }

        #endregion

        private void InitializeODEWorld()
        {
            world = Ode.dWorldCreate();
            space = Ode.dHashSpaceCreate(space);

            contactgroup = Ode.dJointGroupCreate(0);
            contacts = new Ode.dContactGeom[8];

            floor = new Grid(new Vector(0.0, 0.0, 0.1), 20, 20, false);
            Ode.dCreatePlane(space, 0.0f, 1.0f, 0.0f, 0.0f);

            Ode.dWorldSetGravity(world, 0.0f, -9.81f, 0);

            Ode.dWorldSetERP(world, 0.2f);
            Ode.dWorldSetCFM(world, 1e-5f);
            //Ode.dWorldSetAutoDisableFlag(world, 1);
            Ode.dWorldSetContactMaxCorrectingVel(world, 0.1f);
            Ode.dWorldSetContactSurfaceLayer(world, 0.001f);

            myBox1 = new Quad(world, space, 1.0f,
              new Ode.dVector3(0.0f, 0.5f, 0.0f),
              new Ode.dVector3(1.0f, 1.0f, 1.0f),
              new Ode.dVector3(0.0f, 0.5f, 0.0f));

            myBox2 = new Quad(world, space, 1.0f,
              new Ode.dVector3(-0.85f, 3.0f, 0.0f),
              new Ode.dVector3(1.0f, 1.0f, 1.0f),
              new Ode.dVector3(0.0f, 0.0f, 0.5f));

            //myJoint = Ode.dJointCreateHinge(world, jointgroup);
            //Ode.dJointAttach(myJoint, myBall.Body, myBall2.Body);
            //Ode.dJointSetHingeAnchor(myJoint, 0.0f, 4.1f, 0.0f);

        }

        private void startButton_Click(object sender, EventArgs e)
        {
            float time = 0;
            float deltaTime = 0.04f;

            while (time < (int)simTimeUpDown.Value)
            {
                currTimeTextBox.Text = time.ToString();

                currTimeTextBox.Refresh();

                simpleOpenGlControl1.Refresh();

                Ode.dSpaceCollide(space, new IntPtr(), new Ode.dNearCallback(near));
                Ode.dWorldStep(world, deltaTime);
                Ode.dJointGroupEmpty(contactgroup);

                time += deltaTime;
            }
        }

        static void near(IntPtr data, IntPtr o1, IntPtr o2)
        {
            IntPtr b1 = Ode.dGeomGetBody(o1);
            IntPtr b2 = Ode.dGeomGetBody(o2);

            int MAX_COLLISIONS = 20;

            Ode.dContactGeom[] contactGeoms = new Ode.dContactGeom[MAX_COLLISIONS];

            int numc = Ode.dCollide(o1, o2, MAX_COLLISIONS, contactGeoms,
                System.Runtime.InteropServices.Marshal.SizeOf(contactGeoms[0]));

            Ode.dContact[] contact = new Ode.dContact[numc];

            for (int i = 0; i < numc; i++)
            {
                contact[i].surface.mode =
                    (int)Ode.dContactFlags.dContactBounce |
                    (int)Ode.dContactFlags.dContactSoftCFM;

                contact[i].surface.mu = 2.1f;
                contact[i].surface.mu2 = 2.1f;
                contact[i].surface.bounce = 0.8f;
                contact[i].surface.bounce_vel = 0.6f;
                contact[i].surface.soft_cfm = 0;

                contact[i].geom = contactGeoms[i];

                IntPtr joint =
                    Ode.dJointCreateContact(world, contactgroup, ref contact[i]);

                Ode.dJointAttach(joint, b1, b2);
            }
        }

        private void resetButton_Click(object sender, EventArgs e)
        {
            InitializeODEWorld();
            simpleOpenGlControl1.Refresh();
        }

        #region View Controls

        private void xRotScrollBar_Scroll(object sender, ScrollEventArgs e)
        {
            xRotTextBox.Text = xRotScrollBar.Value.ToString();
            simpleOpenGlControl1.Refresh();
        }

        private void yRotScrollBar_Scroll(object sender, ScrollEventArgs e)
        {
            yRotTextBox.Text = yRotScrollBar.Value.ToString();
            simpleOpenGlControl1.Refresh();
        }

        private void zRotScrollBar_Scroll(object sender, ScrollEventArgs e)
        {
            zRotTextBox.Text = zRotScrollBar.Value.ToString();
            simpleOpenGlControl1.Refresh();
        }

        private void xTransScrollBar_Scroll(object sender, ScrollEventArgs e)
        {
            xTransTextBox.Text = xTransScrollBar.Value.ToString();
            simpleOpenGlControl1.Refresh();
        }

        private void yTransScrollBar_Scroll(object sender, ScrollEventArgs e)
        {
            yTransTextBox.Text = yTransScrollBar.Value.ToString();
            simpleOpenGlControl1.Refresh();
        }

        private void zTransScrollBar_Scroll(object sender, ScrollEventArgs e)
        {
            zTransTextBox.Text = zTransScrollBar.Value.ToString();
            simpleOpenGlControl1.Refresh();
        }

        private void viewResetButton_Click(object sender, EventArgs e)
        {
            xRotScrollBar.Value = 0;
            yRotScrollBar.Value = 0;
            zRotScrollBar.Value = 0;

            xRotTextBox.Text = "0";
            yRotTextBox.Text = "0";
            zRotTextBox.Text = "0";

            xTransScrollBar.Value = 0;
            yTransScrollBar.Value = 0;
            zTransScrollBar.Value = 0;

            xTransTextBox.Text = "0";
            yTransTextBox.Text = "0";
            zTransTextBox.Text = "0";

            this.Refresh();
        }

        #endregion









    }
}